package GameEngine;

/**
 * This is main Class Board which sets up the Game board and provides the rules for the game
 * @author Io
 * @version 21. July 2009
 */
public class Board implements Cloneable {

    /**
     *  the first [] is the vertical and the second [] is the horizontal
     */
    private static int boardSize = 6;
    private int board[][] = new int[boardSize][boardSize];
    /* boardclone is used fot IA */
    private static int boardclone[][] = new int[boardSize][boardSize];

    /**
     * Flag to determine which players turn it is
     * The flag switches from -1 to 1 every turn (-1 = black player; 1 = red player)
     */
    private short playerFlag = -1;

    /**
     * Status object for the actual Board
     */
    private Status actStatus = new Status(0);
    private int turnsLeft = boardSize * boardSize, turnsBlack = turnsLeft / 2, turnsRed = turnsLeft / 2;

    /**
     * Constructor: empty board
     */
    public Board() {
        for (int i = 0; i < boardSize; i++) {
            for (int j = 0; j < boardSize; j++) {
                board[i][j] = 0;
                boardclone[i][j] = 0;
            }
        }
    }

//####################### GETTER METHODS ################################################
    /**
     * This method returns on of the four Quadrants as an int from 1 to 4 If no
     * Quadrant is passed, the method will return 0
     *
     * @param ver vertical coordinate of the board
     * @param hor hoizontal coordinate of the board
     * @return int returns the quadrant (1-4) as Integer
     */
    	//no need to subtract 1 at the coordinates because this is done by the calling functions
    public int getQuadrant(int ver, int hor) {
        if ((ver >= 0 && ver <= 2) && (hor >= 0 && hor <= 2)) {
            return 1;
        }
        if ((ver >= 3 && ver <= 5) && (hor >= 0 && hor <= 2)) {
            return 2;
        }
        if ((ver >= 0 && ver <= 2) && (hor >= 3 && hor <= 5)) {
            return 3;
        }
        if ((ver >= 3 && ver <= 5) && (hor >= 3 && hor <= 5)) {
            return 4;
        }
        return 0;
    }

    /**
     * This Method returns the board instance array
     * needed for the video
     * @return int[][] board
     */
    public int[][] getBoard() {
    	return board;
    }

    /**
     * This method returns a complete clone of the BoardArrays at its actual
     * needed for the AI
     *
     * @return int[][] board clone
     */
    public static int[][] getBoardClone() {
        return boardclone.clone();
    }

    /**
     * This Method returns the actual Player using
     * TurnsLeft mod 2 = true (black) else false (red)
     * @return actual player
     */
    public boolean getPlayer() {
    	return playerFlag == -1;
    }

    /**
     * This Method returns an int array including the turn informations on this board
     * [0] all turns left
     * [1] black turns left
     * [2] red turns left
     * @return int turn
    */
    public int getTurns() {
        //int[] turn = {this.turnsLeft, this.turnsBlack, this.turnsRed};
        return this.turnsLeft;
    }

    /**
     * This Method returns the actual board status
     * @return Status actStatus
    */
    public Status getStatus() {
        return actStatus;
    }

//############ SETTER METHODS #############################################
    /**
     * This Method sets the Marble onto the board Each Marble will be validated
     * decrement the board turns and the player turns
     *
     * @param ver vertical coordinate of the board
     * @param hor horizontal coordinate of the board
     * @param color of the Marble (1 red, -1 black)
     */
    public void setMarble(int ver, int hor, boolean color) {
        int icolor;
        ver -= 1;
        hor -= 1;
        if (color) {
            icolor = -1;
        } else {
            icolor = 1;
        }

        this.board[ver][hor] = icolor;
        boardclone[ver][hor] = icolor;

    }

//############### FUNCTIONS ############################################
    /**
     *  Overwrites the toString to our needs
     *  function included in Java.Object
     * @return String of the board
     */
    public String toString() {
        String s = "Overall turns left: " + this.turnsLeft+"\nBlack turns left: " 
                + this.turnsBlack+"\nRed turns left: " + this.turnsRed
                +"\n<--------------------------------->"+"\nActual board:\n[\\]";
        String player = " ";
        for (int i = 1; i <= boardSize; i++) {
            //s += " " + i + "  ";
            s += "[" + i + "]";
        }
        for (int i = 0; i < boardSize; i++) {
            s += "\n[" + (i + 1) + "]";
            for (int j = 0; j < boardSize; j++) {
                if (this.board[i][j] == 1) {
                    player = "#";
                } else if (this.board[i][j] == -1) {
                    player = "@";
                } else {
                    player = " ";
                }
                s += "(" + player + ")";// + " (Zeile) "+i;
            }
        }
        return (s);
    }

//################## CHECK WIN ###############################################

    /**
     * This function recognizes that the input was empty
     * The player who enters a empty input looses automatically this game
     * The opponent wins this session
     * @param obj Consists all data of the move
     * @param who This parameter shows who is playing. If who = true, Black plays, else Red plays
     */
    public void checkResign(Move obj, boolean who) {
        if ((obj == null) && who) {
            System.out.println("Black resigns!");
            System.exit(0);
        }
        if ((obj == null) && !who) {
            System.out.println("Red resigns!");
            System.exit(0);
        }

    }

    /**
     * This function calculates the sum of the columns
     * @param who This parameter shows who is playing. If who = true, Black plays, else Red plays
     */
    public void checkWinColumn(boolean who) {

        for (int i = 0; i < 6; i++) {
            // initialize sum with 0
            int sum = 0;
            for (int j = 0; j < 6; j++) {
                if (who) {
                    if (board[j][i] == 1) {
                        sum += board[j][i];
                    } else {
                        sum = 0;
                    }
                    if (sum == 5) {
                        actStatus.value(2);
                    }
                } else if (!who) {

                    if (board[j][i] == -1) {
                        sum += board[j][i];
                    } else {
                        sum = 0;
                    }

                    if (sum == -5) {
                        actStatus.value(1);
                    }
                }
            }
        }


    }

    /**
     * This function calculates the sum of the rows
     * @param who This parameter shows who is playing. If who = true, Black plays, else Red plays
     */
    public void checkWinRow(boolean who) {

        for (int i = 0; i < 6; i++) {
            // initialize sum with
            int sum = 0;
            for (int j = 0; j < 6; j++) {
                if (who) {
                    if (board[i][j] == 1) {
                        sum += board[i][j];
                    } else {
                        sum = 0;
                    }
                    if (sum == 5) {
                        actStatus.value(2);
                    }
                } else if (!who) {
                    if (board[i][j] == -1) {
                        sum += board[i][j];
                    } else {
                        sum = 0;
                    }

                    if (sum == -5) {
                        actStatus.value(1);
                    }
                }

            }
        }


    }

    /**
     * This function calculates the sum of the 3 second diagonals
     * @param who This parameter shows who is playing. If who = true, Black plays, else Red plays
     */
    public void checkWinSecondDiag(boolean who) {
        int sum = 0;
        // Diagonal from 0|5 to 5|0
        for (int i = 0; i < 6; i++) {
            int j = 5 - i;

            if (who) {
                if (board[j][i] == 1) {
                    sum += board[j][i];
                } else {
                    sum = 0;
                }
                if (sum == 5) {
                    actStatus.value(2);
                }
            } else if (!who) {

                if (board[j][i] == -1) {
                    sum += board[j][i];
                } else {
                    sum = 0;
                }

                if (sum == -5) {
                    actStatus.value(1);
                }
            }
        }

        // Diagonal from 0|4 to 4|0
        sum = 0;
        for (int i = 0; i < 5; i++) {
            int j = 4 - i;

            if (who) {
                if (board[j][i] == 1) {
                    sum += board[j][i];
                } else {
                    sum = 0;
                }
                if (sum == 5) {
                    actStatus.value(2);
                }
            } else if (!who) {

                if (board[j][i] == -1) {
                    sum += board[j][i];
                } else {
                    sum = 0;
                }

                if (sum == -5) {
                    actStatus.value(1);
                }
            }
        }

        // Diagonal from 1|5 to 5|1
        sum = 0;
        for (int i = 1; i < 6; i++) {
            int j = 6 - i;

            if (who) {
                if (board[i][j] == 1) {
                    sum += board[i][j];
                } else {
                    sum = 0;
                }
                if (sum == 5) {
                    actStatus.value(2);
                }
            } else if (!who) {

                if (board[i][j] == -1) {
                    sum += board[i][j];
                } else {
                    sum = 0;
                }

                if (sum == -5) {
                    actStatus.value(1);
                }
            }
        }

    }

    /**
     * This function calculates the sum of the 3 diagonals
     * @param who This parameter shows who is playing. If who = true, Black plays, else Red plays
     */
    public void checkWinMainDiag(boolean who) {

        // Diagonal from 0|0 to 5|5

        // initialize sum with 0
        int sum = 0;
        for (int i = 0; i < 6; i++) {

            if (who) {
                if (board[i][i] == 1) {
                    sum += board[i][i];
                } else {
                    sum = 0;
                }
                if (sum == 5) {
                    actStatus.value(2);
                }
            } else if (!who) {

                if (board[i][i] == -1) {
                    sum += board[i][i];
                } else {
                    sum = 0;
                }

                if (sum == -5) {
                    actStatus.value(1);
                }
            }
        }

        // Diagonal from 0|1 to 4|5

        // initialize sum with 0
        sum = 0;
        for (int i = 0; i < 5; i++) {

            if (who) {
                if (board[i + 1][i] == 1) {
                    sum += board[i + 1][i];
                } else {
                    sum = 0;
                }
                if (sum == 5) {
                    actStatus.value(2);
                }
            } else if (!who) {

                if (board[i + 1][i] == -1) {
                    sum += board[i + 1][i];
                } else {
                    sum = 0;
                }

                if (sum == -5) {
                    actStatus.value(1);
                }
            }
        }

        // Diagonal from 1|0 to 5|4

        // initialize sum with 0
        sum = 0;
        for (int i = 0; i < 5; i++) {

            if (who) {
                if (board[i][i + 1] == 1) {
                    sum += board[i][i + 1];
                } else {
                    sum = 0;
                }
                if (sum == 5) {
                    actStatus.value(2);
                }
            } else if (!who) {

                if (board[i][i + 1] == -1) {
                    sum += board[i][i + 1];
                } else {
                    sum = 0;
                }

                if (sum == -5) {
                    actStatus.value(1);
                }
            }
        }

    }

    /**
     * This is the main function to make a check on the board.
     * It controls the board in this sequence: rows - columns - main diagonals - second diagonls
     * If nobody wins the session and they have no turns anymore, this function sets the status on draw
     * @param who This parameter shows who is playing. If who = true, Black plays, else Red plays
     */
    public void checkWin(boolean who) {
        checkWinRow(who);

        checkWinColumn(who);

        checkWinMainDiag(who);

        checkWinSecondDiag(who);

        if (this.turnsLeft == 0) {
            actStatus.value(3);
        }
    }

    //################## END CHECK WIN ###############################################
    /**
     * isValid check the input parameters. It checks, that the chosen field is
     * empty, that the horizontal and vertical coordinates are on the board
     *
     * @param ver vertical coordinate of the board
     * @param hor horizontal coordinate of the board
     */
    public void isValid(int ver, int hor) {
        ver -= 1;
        hor -= 1;
        if (isFree(ver, hor) && onBoard(ver, hor) && validQuadrand(getQuadrant(ver, hor))) {
            actStatus.value(0);
        } else {
            actStatus.value(4);
        }
    }

    /**
     * isFree checks the input parameters on a field and returns true if its free (=0)
     * @param ver vertical coordinate of the board
     * @param hor horizontal coordinate of the board
     * @return it returns true, if the field is empty
     */

    private boolean isFree(int ver, int hor) {
        // no need to subtract 1 at the coordinates because this is done by the calling functions
        if (board[ver][hor] == 0) {
            return true;
        }
        return false;
    }

    /**
     * This method checks, if the transfered parameter is between 1 and 4
     * Thats important because the quadrant of the board must not out of range
     * from 1 to 4
     *
     * @param quad consists one of possible four quadrants of the board
     * @return if quadrant is between 1 and 4 returns the method true, else false
     */
    private boolean validQuadrand(int quad) {
        if (quad >= 1 && quad <= 4) {
            return true;
        }
        return false;
    }

    /**
     * This method checks, that the given coordinates are an the board.
     *
     * @param ver vertical coordinate of the board
     * @param hor horizontal coordinate of the board
     * @return it returns true, if the coordinates are on the board
     */

    private boolean onBoard(int ver, int hor) {
        // no need to subtract 1 at the coordinates because this is done by the calling functions
        if ((ver < board.length) && (hor < board[0].length)) {
            return true;
        }
        return false;
    }

    /**
     * This method rotates one of the 4 quadrants (1-4) of the board.<br>
     * It constructs a temporary 3x3 matrix with the rotation inside and writes the changes in the game board and his clone.
     *
     * @param quadrant the quadrant <u>(1-4)</u> witch should rotate
     * @param dir the direction of the rotation
     */
    public void rotate(int quadrant, boolean dir) {
        int[][] tempboard = new int[3][3]; // temporary board for constructing
        int clock;
        //change clockwise (boolean) to numeric
        if (dir) {
            clock = 1;
        } else {
            clock = -1;
        }
        // the rotation
        int fixx = 0; 		// variable for the x position of the fix point
        int fixy = 0; 		// variable for the y position of the fix point
        switch (quadrant) { // classify coordinates to fix point and fix it
            case 1: {
                fixy = 1;
                fixx = 1;
            }
            break;
            case 2: {
                fixy = 1;
                fixx = 4;
            }
            break;
            case 3: {
                fixy = 4;
                fixx = 1;
            }
            break;
            case 4: {
                fixy = 4;
                fixx = 4;
            }
            break;
        }

         // The Board is at this moment based on this theory:<br>
         // [xx] are fix points<br>
         // (xx) are rotating points<br>
         // <br>
         // (00)(01)(02)(03)(04)(05)<br>
         // (06)[07](08)(09)[10](11)<br>
         // (12)(13)(14)(15)(16)(17)<br>
         // (18)(19)(20)(21)(22)(23)<br>
         // (24)[25](26)(27)[28](29)<br>
         // (30)(31)(32)(33)(34)(35)<br>
         // <br>
         // Based on this board the function is now able to do the rotation in the 3x3 matrix with fix point f and clock:
         // <ul>
         //  <li>clockwise<br>
         //      (f+5)(f-1)(f-7)<br>
         //      (f+6)[-f-](f-6)<br>
         //      (f+7)(f+1)(f-5)<br>
         // </li>
         // <li>or non clockwise<br>
         //     (f-5)(f+1)(f+7)<br>
         //     (f-6)[-f-](f+6)<br>
         //     (f-7)(f-1)(f+5)<br>
         // </li>
         //

        int fix = (fixy * 6) + fixx; // numeric value of the fix point
        int j = 5; // first variable for shifting
        int ver = 0, hor = 0;

        // shifting
        for (int i = 0; i <= 2; i++, j++) {
            ver = (fix + (j * clock)) / 6;
            hor = (fix + (j * clock)) % 6;
            tempboard[i][0] = this.board[ver][hor];
        }
        j = -1; // second variable for shifting

        // shifting
        for (int i = 0; i <= 2; i++, j++) {
            ver = (fix + (j * clock)) / 6;
            hor = (fix + (j * clock)) % 6;
            tempboard[i][1] = this.board[ver][hor];
        }
        j = 7; // third variable for shifting

        // shifting
        for (int i = 0; i <= 2; i++, j--) {
            ver = (fix - (j * clock)) / 6;
            hor = (fix - (j * clock)) % 6;
            tempboard[i][2] = this.board[ver][hor];
        }

        // writing the rotation in the game board
        int next = fix - 7; // next is the numeric value for writing
        for (int i = 0; i <= 2; i++) {
            for (j = 0; j <= 2; j++) {
                ver = next / 6;
                hor = next % 6;
                this.board[ver][hor] = tempboard[i][j];
                boardclone[ver][hor] = tempboard[i][j];
                next++;
            }
            next += 3;//go to the next field on this quadrant
        }
        this.turnsLeft--;
        if (playerFlag == -1) this.turnsBlack--;
        else this.turnsRed--;
        // decrement turnsleft
        // switch the playerFlag
        playerFlag *= -1;
    }
}
